package com.such.kitx.tax.impl.aisino;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import com.such.kit.Callable;
import com.such.kit.Logger;
import com.such.kit.easyparam.ParamMap;
import com.such.kit.io.source.MemoryIOSource;
import com.such.kit.io.template.XmlIOTemplate;
import com.such.kit.remote.WebServiceCommons;
import com.such.kit.string.StringCommons;
import com.such.kit.validate.ValidateCommons;
import com.such.kitx.tax.TaxHelper;
import com.such.kitx.tax.TaxInterface;
import com.such.kitx.tax.impl.AbstractTax;

public class AisinoTax extends AbstractTax {

	private static String GG_TRes = "GG_TRes.xml";
	private static String GG_FPBS_TRes = "GG_FPBS_TRes.xml";
	private static String KJFP_TReq = "KJFP_TReq.xml";
	private static String KJFP_TRes = "KJFP_TRes.xml";
	private static String DYSZ_TReq = "DYSZ_TReq.xml";
	private static String DYFP_TReq = "DYFP_TReq.xml";
	private static String DYFP_QD_TReq = "DYFP_QD_TReq.xml";
	private static String ZFFP_TReq = "ZFFP_TReq.xml";
	private static String ZFFP_KB_TReq = "ZFFP_KB_TReq.xml";
	private static String FPSC_TReq = "FPSC_TReq.xml";
	private static String FPSC_TRes = "FPSC_TRes.xml";
	private static String FPGX_TReq = "FPGX_TReq.xml";
	private static String FPGX_TRes = "FPGX_TRes.xml";
	private static String CB_TReq = "CB_TReq.xml";
	private static String QK_TReq = "QK_TReq.xml";
	private static String SCHZ_TReq = "SCHZ_TReq.xml";
	private static String SCHZ_TRes = "SCHZ_TRes.xml";

	private String address;
	private String clientIP;
	private String clientPort;

	/**
	 * @param interfaceVersion 接口版本
	 * @param address 服务地址，非 WSDL 地址 [举例] [http | https]://xxx.xxx.xxx/xxx.asmx
	 */
	public AisinoTax(String interfaceVersion, String address, String clientIP, String clientPort) {
		super(TaxInterface.TAX_VENDOR_AISINO, interfaceVersion);
		this.address = address;
		this.clientIP = clientIP;
		this.clientPort = clientPort;

		addTemplateMap("KJFP", KJFP_TReq, KJFP_TRes);
		addTemplateMap("DYSZ", DYSZ_TReq, GG_TRes);
		addTemplateMap("DYFP", DYFP_TReq, DYFP_QD_TReq, GG_FPBS_TRes);
		addTemplateMap("ZFFP", ZFFP_TReq, GG_FPBS_TRes);
		addTemplateMap("FPSC", FPSC_TReq, FPSC_TRes);
		addTemplateMap("FPGX", FPGX_TReq, FPGX_TRes);
		addTemplateMap("CB", CB_TReq, GG_TRes);
		addTemplateMap("QK", QK_TReq, GG_TRes);
		addTemplateMap("SCHZ", SCHZ_TReq, SCHZ_TRes);
	}

	/**
	 * <pre>
	 * [参考 {@link TaxInterface#KJFP(Map...)}]
	 * 
	 * 航信的税控服务在完成开具后会自动进行实时上传，并轮询检查发票状态更新到客户端
	 * </pre>
	 * @see TaxInterface#KJFP(java.util.Map[])
	 */
	public List<Map<String, Object>> KJFP(Map<String, Object>... paramsArray) throws Exception {
		return invokeByModeA(KJFP_TReq, new Callable<Map<String,Object>, Boolean>() {
			public Boolean call(Map<String, Object> params) throws Exception {
				MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
				Object FPMX = params.get("FPMX"); // 发票明细，存放票面相关的明细 [扩展字段]
				List<Map<String, Object>> fpmxs = null;
				if (FPMX instanceof List) {
					fpmxs = (List<Map<String, Object>>) FPMX;
				}
				if (ValidateCommons.isNotEmpty(fpmxs)) {
					params.put("LOGIC_ID", UUID.randomUUID().toString());
					reqSource.setOutputResult("record", null, params);

					for (Map<String, Object> fpmx : fpmxs) {
						reqSource.setOutputResult("fpmx_#[LOGIC_ID]", params, fpmx);
					}
				}
				return true;
			}
		}, KJFP_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
			public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
				return resSource.getInputResult("refp");
			}
		}, "issueInvoice", paramsArray);
	}

	public List<Map<String, Object>> DYSZ(Map<String, Object>... paramsArray) throws Exception {
		return invokeByModeB(DYSZ_TReq, new Callable<Map<String,Object>, Boolean>() {
			public Boolean call(Map<String, Object> params) throws Exception {
				MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
				reqSource.setOutputResult("data", null, params);
				return true;
			}
		}, GG_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
			public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
				return resSource.getInputResult("err");
			}
		}, "printParams", paramsArray);
	}

	/**
	 * <pre>
	 * [参考 {@link TaxInterface#DYFP(Map...)}]
	 * 
	 * 航信的税控服务对打印、清单打印采用了 2 个不同接口，TaxInterface 对其进行了整合，通过指定的保留字判断具体使用哪种打印方式
	 * 
	 * [保留字] 列表
	 *   DYLX [默认值为 0] [0 打印 | 1 清单打印]
	 * </pre>
	 * @see TaxInterface#DYFP(java.util.Map[])
	 */
	public List<Map<String, Object>> DYFP(Map<String, Object>... paramsArray) throws Exception {
		List<Map<String, Object>> dyList = new ArrayList<Map<String,Object>>();
		List<Integer> dyOrderList = new ArrayList<Integer>();
		List<Map<String, Object>> qddyList = new ArrayList<Map<String,Object>>();
		List<Integer> qddyOrderList = new ArrayList<Integer>();
		List<Map<String, Object>> results = new ArrayList<Map<String,Object>>();
		if (ValidateCommons.isNotEmpty(paramsArray)) {
			for (int i = 0; i < paramsArray.length; i++) {
				Map<String, Object> params = paramsArray[i];
				// 分离打印与清单打印
				Object DYLX = params.get("DYLX");
				if (ValidateCommons.isEmpty(DYLX) || "0".equals(DYLX)) {
					dyList.add(params);
					dyOrderList.add(i);
				} else if ("1".equals(DYLX)) {
					qddyList.add(params);
					qddyOrderList.add(i);
				}
			}
		}

		if (ValidateCommons.isNotEmpty(dyList)) {
			List<Map<String, Object>> dyResults = invokeByModeA(DYFP_TReq, new Callable<Map<String,Object>, Boolean>() {
				public Boolean call(Map<String, Object> params) throws Exception {
					MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
					reqSource.setOutputResult("record", null, params);
					return true;
				}
			}, GG_FPBS_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
				public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
					return resSource.getInputResult("refp");
				}
			}, "printInvoice", dyList.toArray(new Map[0]));
			if (ValidateCommons.isNotEmpty(dyResults)) {
				for (int i = 0; i < dyResults.size(); i++) {
					Map<String, Object> result = dyResults.get(i);
					int index = dyOrderList.get(i);

					results.add(index, result);
				}
			}
		}
		if (ValidateCommons.isNotEmpty(qddyList)) {
			List<Map<String, Object>> qddyResults = invokeByModeA(DYFP_QD_TReq, new Callable<Map<String,Object>, Boolean>() {
				public Boolean call(Map<String, Object> params) throws Exception {
					MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
					reqSource.setOutputResult("record", null, params);
					return true;
				}
			}, GG_FPBS_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
				public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
					return resSource.getInputResult("refp");
				}
			}, "printList", qddyList.toArray(new Map[0]));
			if (ValidateCommons.isNotEmpty(qddyResults)) {
				for (int i = 0; i < qddyResults.size(); i++) {
					Map<String, Object> result = qddyResults.get(i);
					int index = dyOrderList.get(i);

					results.add(index, result);
				}
			}
		}
		return results;
	}

	/**
	 * <pre>
	 * [参考 {@link TaxInterface#ZFFP(Map...)}]
	 * 
	 * 航信的税控服务发票作废、空白发票作废采用了 2 个不同接口，TaxInterface 对其进行了整合，通过指定的保留字判断具体使用哪种作废
	 * 
	 * [保留字] 列表
	 *   ZFLX [默认值为 1] [0 空白发票作废 | 1 已开发票作废]
	 * </pre>
	 * @see TaxInterface#ZFFP(java.util.Map[])
	 */
	public List<Map<String, Object>> ZFFP(Map<String, Object>... paramsArray) throws Exception {
		List<Map<String, Object>> zfList = new ArrayList<Map<String,Object>>();
		List<Integer> zfOrderList = new ArrayList<Integer>();
		List<Map<String, Object>> kbzfList = new ArrayList<Map<String,Object>>();
		List<Integer> kbzfOrderList = new ArrayList<Integer>();
		List<Map<String, Object>> results = new ArrayList<Map<String,Object>>();
		if (ValidateCommons.isNotEmpty(paramsArray)) {
			for (int i = 0; i < paramsArray.length; i++) {
				Map<String, Object> params = paramsArray[i];
				// 分离已开作废与空白作废
				Object ZFLX = params.get("ZFLX");
				if (ValidateCommons.isEmpty(ZFLX) || "1".equals(ZFLX)) {
					zfList.add(params);
					zfOrderList.add(i);
				} else if ("0".equals(ZFLX)) {
					kbzfList.add(params);
					kbzfOrderList.add(i);
				}
			}
		}

		if (ValidateCommons.isNotEmpty(zfList)) {
			List<Map<String, Object>> zfResults = invokeByModeA(ZFFP_TReq, new Callable<Map<String,Object>, Boolean>() {
				public Boolean call(Map<String, Object> params) throws Exception {
					MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
					reqSource.setOutputResult("record", null, params);
					return true;
				}
			}, GG_FPBS_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
				public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
					return resSource.getInputResult("refp");
				}
			}, "invalidInvoice", zfList.toArray(new Map[0]));
			if (ValidateCommons.isNotEmpty(zfResults)) {
				for (int i = 0; i < zfResults.size(); i++) {
					Map<String, Object> result = zfResults.get(i);
					int index = zfOrderList.get(i);

					results.add(index, result);
				}
			}
		}
		if (ValidateCommons.isNotEmpty(kbzfList)) {
			List<Map<String, Object>> kbzfResults = invokeByModeA(ZFFP_KB_TReq, new Callable<Map<String,Object>, Boolean>() {
				public Boolean call(Map<String, Object> params) throws Exception {
					MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
					reqSource.setOutputResult("record", null, params);
					return true;
				}
			}, GG_FPBS_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
				public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
					return resSource.getInputResult("refp");
				}
			}, "invalidBlankInvoice", kbzfList.toArray(new Map[0]));
			if (ValidateCommons.isNotEmpty(kbzfResults)) {
				for (int i = 0; i < kbzfResults.size(); i++) {
					Map<String, Object> result = kbzfResults.get(i);
					int index = kbzfOrderList.get(i);

					results.add(index, result);
				}
			}
		}
		return results;
	}

	/**
	 * <pre>
	 * [参考 {@link TaxInterface#FPSC(Map...)}]
	 * 
	 * 航信的税控服务中的发票上传需要指定发票号码、发票代码，既指定发票上传
	 * 新版开票客户端在完成开具后会自动实时上传，可以根据事件情况考虑是否使用此接口
	 * </pre>
	 * @see TaxInterface#ZFFP(java.util.Map[])
	 */
	public List<Map<String, Object>> FPSC(Map<String, Object>... paramsArray) throws Exception {
		return invokeByModeA(FPSC_TReq, new Callable<Map<String,Object>, Boolean>() {
			public Boolean call(Map<String, Object> params) throws Exception {
				MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
				reqSource.setOutputResult("record", null, params);
				return true;
			}
		}, FPSC_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
			public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
				return resSource.getInputResult("refp");
			}
		}, "uploadInvoice", paramsArray);
	}

	/**
	 * <pre>
	 * [参考 {@link TaxInterface#FPGX(Map...)}]
	 * 
	 * 航信的税控服务中的发票上传需要指定发票号码、发票代码，既指定发票上传
	 * 新版开票客户端会轮询检查发票状态更新到客户端，可以根据事件情况考虑是否使用此接口
	 * </pre>
	 * @see TaxInterface#ZFFP(java.util.Map[])
	 */
	public List<Map<String, Object>> FPGX(Map<String, Object>... paramsArray) throws Exception {
		return invokeByModeA(FPGX_TReq, null, FPGX_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
			public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
				List<Map<String, Object>> errs = resSource.getInputResult("err");
				List<Map<String, Object>> succs = resSource.getInputResult("succ");
				List<Map<String, Object>> fails = resSource.getInputResult("fail");

				List<Map<String, Object>> results = new ArrayList<Map<String,Object>>();
				if (ValidateCommons.isNotEmpty(errs)) {
					Map<String, Object> err = errs.get(0);
					Object RETCODE = err.get("RETCODE");
					Object RETMSG = err.get("RETMSG");

					if (ValidateCommons.isNotEmpty(succs)) {
						for (Map<String, Object> succ : succs) {
							succ.put("RETCODE", RETCODE);
							succ.put("RETMSG", RETMSG);
						}
						results.addAll(succs);
					}
					if (ValidateCommons.isNotEmpty(fails)) {
						for (Map<String, Object> fail : fails) {
							fail.put("RETCODE", RETCODE);
							fail.put("RETMSG", RETMSG);
						}
						results.addAll(fails);
					}
				}
				return results;
			}
		}, "updateInvoiceStatus", paramsArray);
	}

	public List<Map<String, Object>> CB(Map<String, Object>... paramsArray) throws Exception {
		return invokeByModeB(CB_TReq, null, GG_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
			public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
				return resSource.getInputResult("err");
			}
		}, "copyTax", paramsArray);
	}

	public List<Map<String, Object>> QK(Map<String, Object>... paramsArray) throws Exception {
		return invokeByModeB(QK_TReq, null, GG_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
			public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
				return resSource.getInputResult("err");
			}
		}, "clearCard", paramsArray);
	}

	public List<Map<String, Object>> SCHZ(Map<String, Object>... paramsArray) throws Exception {
		return invokeByModeA(SCHZ_TReq, new Callable<Map<String,Object>, Boolean>() {
			public Boolean call(Map<String, Object> params) throws Exception {
				MemoryIOSource reqSource = (MemoryIOSource) params.remove("reqSource");
				Object FPMX = params.get("FPMX"); // 红字明细，存放红字相关的明细 [扩展字段]
				List<Map<String, Object>> fpmxs = null;
				if (FPMX instanceof List) {
					fpmxs = (List<Map<String, Object>>) FPMX;
				}
				params.put("LOGIC_ID", UUID.randomUUID().toString());

				reqSource.setOutputResult("record", null, params);
				if (ValidateCommons.isNotEmpty(fpmxs)) {
					for (Map<String, Object> fpmx : fpmxs) {
						reqSource.setOutputResult("fpmx_#[LOGIC_ID]", params, fpmx);
					}
				}
				return true;
			}
		}, SCHZ_TRes, new Callable<MemoryIOSource, List<Map<String,Object>>>() {
			public List<Map<String, Object>> call(MemoryIOSource resSource) throws Exception {
				List<Map<String, Object>> errs = resSource.getInputResult("err");
				List<Map<String, Object>> refps = resSource.getInputResult("refp");

				List<Map<String, Object>> results = new ArrayList<Map<String,Object>>();
				if (ValidateCommons.isNotEmpty(errs)) {
					Map<String, Object> err = errs.get(0);
					Object RETCODE = err.get("RETCODE");
					Object RETMSG = err.get("RETMSG");

					if (ValidateCommons.isNotEmpty(refps)) {
						for (Map<String, Object> refp : refps) {
							refp.put("RETCODE", RETCODE);
							refp.put("RETMSG", RETMSG);
						}
						results.addAll(refps);
					}
				}
				return results;
			}
		}, "uploadRedInfo", paramsArray);
	}

	/**
	 * <pre>
	 * 批量处理模式执行
	 * </pre>
	 */
	private List<Map<String, Object>> invokeByModeA(
			String xmlTReq, Callable<Map<String, Object>, Boolean> reqCaller, 
			String xmlTRes, Callable<MemoryIOSource, List<Map<String, Object>>> resCaller, 
			String method, Map<String, Object>... paramsArray) throws Exception {
		if (ValidateCommons.isNotEmpty(paramsArray)) {
			int paramsLen = paramsArray.length;

			XmlIOTemplate reqTemplate = TaxHelper.getXmlIOTemplate(super.taxVendor, super.interfaceVersion, xmlTReq);
			reqTemplate.putArgs(new ParamMap<String, Object>()
					.putParam("IP", this.clientIP)
					.putParam("PORT", this.clientPort)
					.putParam("COUNT", paramsLen));
			MemoryIOSource reqSource = new MemoryIOSource();
			reqSource.clearOutput();
			XmlIOTemplate resTemplate = TaxHelper.getXmlIOTemplate(super.taxVendor, super.interfaceVersion, xmlTRes);
			MemoryIOSource resSource = new MemoryIOSource();
			resSource.clearInput();

			if (ValidateCommons.isNotEmpty(reqCaller)) {
				for (int i = 0; i < paramsLen; i++) {
					Map<String, Object> params = paramsArray[i];
					params.put("reqSource", reqSource);

					reqCaller.call(params);
				}
			}
			String reqXml = reqTemplate.output(reqSource);
			Logger.debug(getClass(), StringCommons.merger("Aisino 请求报文：", StringCommons.NEW_LINE, reqXml));

			LinkedHashMap<String, String> reqParams = new LinkedHashMap<String, String>();
			reqParams.put("xml", reqXml);
			String resXml = WebServiceCommons.invoke(this.address, method, reqParams);
			Logger.debug(getClass(), StringCommons.merger("Aisino 响应报文：", StringCommons.NEW_LINE, resXml));
			resTemplate.input(resSource, resXml, StringCommons.CHARSET_GBK);
			if (ValidateCommons.isNotEmpty(resCaller)) {
				return resCaller.call(resSource);
			}
		}
		return null;
	}

	/**
	 * <pre>
	 * 单条处理模式执行
	 * </pre>
	 */
	private List<Map<String, Object>> invokeByModeB(
			String xmlTReq, Callable<Map<String, Object>, Boolean> reqCaller, 
			String xmlTRes, Callable<MemoryIOSource, List<Map<String, Object>>> resCaller, 
			String method, Map<String, Object>... paramsArray) throws Exception {
		if (ValidateCommons.isNotEmpty(paramsArray)) {
			int paramsLen = paramsArray.length;

			XmlIOTemplate reqTemplate = TaxHelper.getXmlIOTemplate(super.taxVendor, super.interfaceVersion, xmlTReq);
			reqTemplate.putArgs(new ParamMap<String, Object>()
					.putParam("IP", this.clientIP)
					.putParam("PORT", this.clientPort)
					.putParam("COUNT", paramsLen));
			MemoryIOSource reqSource = new MemoryIOSource();
			XmlIOTemplate resTemplate = TaxHelper.getXmlIOTemplate(super.taxVendor, super.interfaceVersion, xmlTRes);
			MemoryIOSource resSource = new MemoryIOSource();

			List<Map<String, Object>> results = new ArrayList<Map<String,Object>>();
			for (int i = 0; i < paramsLen; i++) {
				Map<String, Object> params = paramsArray[i];
				params.put("reqSource", reqSource);
				reqSource.clearOutput();
				resSource.clearInput();

				if (ValidateCommons.isNotEmpty(reqCaller)) {
					reqCaller.call(params);
				}
				String reqXml = reqTemplate.output(resSource);
				Logger.debug(getClass(), StringCommons.merger("Aisino 请求报文：", StringCommons.NEW_LINE, reqXml));

				LinkedHashMap<String, String> reqParams = new LinkedHashMap<String, String>();
				reqParams.put("xml", reqXml);
				String resXml = WebServiceCommons.invoke(this.address, method, reqParams);
				Logger.debug(getClass(), StringCommons.merger("Aisino 响应报文：", StringCommons.NEW_LINE, resXml));
				resTemplate.input(resSource, resXml, StringCommons.CHARSET_GBK);
				if (ValidateCommons.isNotEmpty(resCaller)) {
					List<Map<String, Object>> _results = resCaller.call(resSource);
					if (ValidateCommons.isNotEmpty(_results)) {
						results.addAll(_results);
					}
				}
			}
			return results;
		}
		return null;
	}

}
